﻿using Inaction.ServiceContract;
using Inaction.Mapping;
using Inaction.Resource.Properties;
using Inaction.Util;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

namespace Inaction.Core
{
    public class Property<P> : IProperty
    {
        #region IProperty
        object IProperty.DefaultValue
        {
            get { return this.DefaultValue; }
        }

        object IProperty.GetFieldValue(ModelBase t)
        {
            return GetFieldValue(t);
        }

        void IProperty.SetFieldValue(ModelBase t, object value)
        {
            this.SetFieldValue(t, (P)value);
        }

        #endregion

        #region 属性/变量
        private Action<object, P> setValueFunc;
        private Func<object, P> getValueFunc;
        public Type EntityType { get; private set; }
        public int Index { get; internal set; }
        public string Name { get; internal set; }
        public string DisplayName { get; set; }
        public FieldInfo Field { get; internal set; }
        public PropertyInfo PropertyInfo { get; private set; }
        public bool NeedUpdate { get; set; }
        public bool Mapping { get; set; }
        public P DefaultValue { get; set; }
        public Inaction.DbSchame.Column Column { get; set; }
        public Type UnderlyingType { get; internal set; }
        public bool IsKeyWordProperty { get; internal set; }
        public bool IsCreaterProperty { get; internal set; }
        public bool IsCreateTimeProperty { get; private set; }
        public bool IsUpdateTimeProperty { get; private set; }
        public bool IsUpdaterProperty { get; private set; }
        public bool IsDeletedProperty { get; private set; }

        List<ValidationAttribute> validateAttributes = null;
        public List<ValidationAttribute> ValidateAttributes
        {
            get { return validateAttributes; }
        }
        RequiredAttribute required = null;
        public RequiredAttribute Required
        {
            get { return required; }
            private set { required = value; }
        }

        #endregion

        #region 构造
        internal Property(ClassInfo classInfo, PropertyInfo property, Func<P> func) :
            this(classInfo, property, func.Invoke())
        {

        }

        internal Property(ClassInfo classInfo, PropertyInfo property, P defaultValue)
        {
            Name = property.Name;
            DefaultValue = defaultValue;

            var attributes = Attribute.GetCustomAttributes(property);
            var displayName = (DisplayNameAttribute)attributes.FirstOrDefault(p => p.GetType().Equals(typeof(DisplayNameAttribute)));
            if(displayName!=null)
            {
                DisplayName = displayName.DisplayName;
            }
            this.PropertyInfo = property;
            UnderlyingType = Util.TypeConvert.GetUnderlyingType(typeof(P));

            #region 字段
            var fieldName = property.Name[0].ToString().ToLower() + property.Name.Substring(1);
            var field = property.DeclaringType.GetField(fieldName, BindingFlags.GetField | BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic);
            if (field == null)
            {
                fieldName = "_" + fieldName;
                field = property.DeclaringType.GetField(fieldName, BindingFlags.GetField | BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.NonPublic);
            }
            if (field == null)
            {
                throw new Exception(string.Format(Resources.NoFieldMappingWithProperty, property.Name));
            }
            Field = field;
            #endregion

            #region 属性描述
            if (IsCreaterProperty == true || Name.Equals(PropertyKeyWord.Creater, StringComparison.OrdinalIgnoreCase))
            {
                IsKeyWordProperty = IsCreaterProperty = true;
                classInfo.CreaterProperty = this as Property<string>;
            }
            else if (IsCreateTimeProperty == true || Name.Equals(PropertyKeyWord.CreateTime, StringComparison.OrdinalIgnoreCase))
            {
                IsKeyWordProperty = IsCreateTimeProperty = true;
                classInfo.CreateTimeProperty = this as Property<DateTime?>;
            }
            else if (IsUpdateTimeProperty == true || Name.Equals(PropertyKeyWord.UpdateTime, StringComparison.OrdinalIgnoreCase))
            {
                IsKeyWordProperty = IsUpdateTimeProperty = true;
                classInfo.UpdateTimeProperty = this as Property<DateTime?>;
            }
            else if (IsUpdaterProperty == true || Name.Equals(PropertyKeyWord.Updater, StringComparison.OrdinalIgnoreCase))
            {
                IsKeyWordProperty = IsUpdaterProperty = true;
                classInfo.UpdaterProperty = this as Property<string>;
            }
            else if (IsDeletedProperty == true || Name.Equals(PropertyKeyWord.Deleted, StringComparison.OrdinalIgnoreCase))
            {
                IsKeyWordProperty = IsDeletedProperty = true;
                classInfo.DeletedProperty = this as Property<bool?>;
                this.DefaultValue = (P)TypeConvert.ConvertToSystemValue(UnderlyingType, false);
            }
            #endregion

            #region 列信息
            var columnInfo = (ColumnAttribute)attributes.FirstOrDefault(p => p.GetType().Equals(typeof(ColumnAttribute)));
            if (columnInfo == null)
            {
                columnInfo = new ColumnAttribute(property.Name);
            }
            Column = classInfo.Table.Columns.FirstOrDefault(p => p.Name.Equals(columnInfo.ColumnName, StringComparison.OrdinalIgnoreCase));
            if (Column != null)
            {
                if (DefaultValue == null && !Column.IsPrimaryKey)
                {
                    if (Column.DefaultValue != DBNull.Value && !string.IsNullOrEmpty(Column.DefaultValue.ToString()))
                    {
                        DefaultValue = (P)TypeConvert.ConvertToSystemValue(UnderlyingType, Column.DefaultValue);
                    }
                }

                NeedUpdate = columnInfo.NeedUpdate;
                Mapping = columnInfo.Mapping;                
                if (string.IsNullOrEmpty(DisplayName))
                {
                    DisplayName = Column.DisplayName;
                }
            }
            #endregion

            #region 设置数据规则
            validateAttributes = attributes.Where(p => p.GetType().IsSubclassOf(typeof(ValidationAttribute))).Cast<ValidationAttribute>().ToList();

            var temp = ValidateAttributes.FirstOrDefault(p => p is RequiredAttribute);
            required = temp == null ? null : (RequiredAttribute)temp;
            if (Column != null && Column.Table.Name == classInfo.TableName)
            {
                if (required == null && !Column.Nullable)
                {
                    required = new RequiredAttribute()
                    {
                        AllowEmptyStrings = false
                    };
                    ValidateAttributes.Add(required);
                }

                if (!Column.IsForeignKey && !Column.IsPrimaryKey)
                {
                    if (UnderlyingType.IsNumber() && !ValidateAttributes.Any(p => p is RangeAttribute))
                    {
                        var maxValue = GetNumericMaxValue();
                        if (maxValue != null)
                        {
                            var minValue = GetNumericMinValue();
                            ValidateAttributes.Add(new RangeAttribute(UnderlyingType, minValue.ToString(), maxValue.ToString()));
                        }
                    }
                    if (UnderlyingType.IsString() && !ValidateAttributes.Any(p => p is StringLengthAttribute))
                    {
                        if (Column.Length.HasValue)
                        {
                            ValidateAttributes.Add(new StringLengthAttribute(Column.Length.Value));
                        }
                    }
                }
            }
            #endregion

            Index = classInfo.Properties.Count;

            if (string.IsNullOrEmpty(DisplayName))
            {
                DisplayName = property.Name;
            }

            EntityType = classInfo.EntityType;
            classInfo.Properties.Add(this);
        }
        #endregion

        #region 方法
        public void SetRequired( )
        {
            if (Required == null)
            {
                Required = new RequiredAttribute();
            }
            Required.AllowEmptyStrings = false;
        }

        public P GetFieldValue(ModelBase t)
        {
            if (getValueFunc == null) getValueFunc = (obj) => { return (P)Field.GetValue(obj); };
            return getValueFunc(t);
        }

        public void SetFieldValue(ModelBase t, P value)
        {
            if (setValueFunc == null) setValueFunc = (obj, p) => { Field.SetValue(obj, p); };
            setValueFunc(t, value);
        }

        public decimal? GetNumericMaxValue()
        {
            if (!Column.SystemType.IsNumber())
            {
                return null;
            }
            var val = Column.SystemType.GetField("MaxValue").GetValue(null).ToString();
            var length = Column.Precision - Column.Scale;
            if (val.Length > length)
            {
                string str = string.Empty;
                while (length != 0)
                {
                    str += "9";
                    length--;
                }
                return decimal.Parse(str);
            }
            return decimal.Parse(val);
        }

        public decimal? GetNumericMinValue()
        {
            if (!Column.SystemType.IsNumber())
            {
                return null;
            }
            var scal = Column.Scale;
            string str = "0";
            if (scal != 0)
            {
                str += ".";
                while (scal != 1)
                {
                    str += "0";
                    scal--;
                }
                str += "1";
            }
            return decimal.Parse(str);
        }
        #endregion
    }
}